/*
 *  Copyright 2001-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package ar.uba.fi.posgrado.academy.service.impl;

import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ar.uba.fi.posgrado.academy.dao.CourseDAO;
import ar.uba.fi.posgrado.academy.dao.CourseTypeDAO;
import ar.uba.fi.posgrado.academy.model.Body;
import ar.uba.fi.posgrado.academy.model.Course;
import ar.uba.fi.posgrado.academy.model.CourseType;
import ar.uba.fi.posgrado.academy.service.CourseService;
import ar.uba.fi.posgrado.common.dao.DAOException;
import ar.uba.fi.posgrado.common.dao.NoSuchEntityException;
import ar.uba.fi.posgrado.common.exception.MissingArgumentException;
import ar.uba.fi.posgrado.common.exception.PosgradoException;
import ar.uba.fi.posgrado.common.exception.PosgradoRuntimeException;
import ar.uba.fi.posgrado.common.exception.RepeatedNameException;
import ar.uba.fi.posgrado.common.model.User;

public class CourseServiceImpl implements CourseService {

	private static Logger logger = Logger.getLogger(CourseServiceImpl.class);

	private CourseTypeDAO courseTypeDAO;

	private CourseDAO courseDAO;

	public CourseDAO getCourseDAO() {
		return courseDAO;
	}

	public void setCourseDAO(CourseDAO courseDAO) {
		this.courseDAO = courseDAO;
	}

	public void setCourseTypeDAO(CourseTypeDAO courseTypeDAO) {
		this.courseTypeDAO = courseTypeDAO;
	}

	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void create(Course course) throws MissingArgumentException,
			PosgradoException {
		if (StringUtils.isBlank(course.getName())
				|| StringUtils.isBlank(course.getCurriculum())
				|| StringUtils.isBlank(course.getCode())
				|| course.getSubjects() == null
				|| course.getSubjects().isEmpty() || course.getBody() == null) {
			String mensaje = "El nombre, el codigo, la curricula, el ente y las materias son datos obligatorios para guardar un curso";
			throw new MissingArgumentException(mensaje);
		}

		this.getCourseDAO().save(course); // usa el dao para crear el objeto
	}

	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void delete(Course t) throws PosgradoRuntimeException,
			PosgradoException {
		this.getCourseDAO().remove(t);
	}

	@Transactional(readOnly = true)
	public List<Course> getAll() throws PosgradoRuntimeException,
			PosgradoException {
		return this.getCourseDAO().findAll();
	}

	@Transactional(readOnly = true)
	public Course getById(Long id) throws PosgradoRuntimeException,
			PosgradoException {
		return (Course) this.getCourseDAO().findById(id);
	}

	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public Course update(Course course) throws PosgradoRuntimeException,
			PosgradoException {
		Course toUpdateCourse = this.getById(course.getId());

		if (StringUtils.isBlank(course.getName())
				|| StringUtils.isBlank(course.getCurriculum())
				|| StringUtils.isBlank(course.getCode())
				|| course.getSubjects() == null
				|| course.getSubjects().isEmpty() || course.getBody() == null) {
			String mensaje = "El nombre, el codigo, la curricula, el ente y las materias son datos obligatorios para guardar un curso";
			throw new MissingArgumentException(mensaje);
		}

		if (!course.getId().equals(toUpdateCourse.getId())) {
			try {
				Course s = this.getById(course.getId());
				if (s.getId() != toUpdateCourse.getId()) {
					throw new RepeatedNameException(
							"No se puede actualizar el curso. Ya hay otro curso en el sistema con el id "
									+ course.getId());
				}
			} catch (NoSuchEntityException e) {
				// significa que esta todo bien y puede actualizar el curso
				toUpdateCourse.setCode(course.getCode());
				toUpdateCourse.setName(course.getName());
				toUpdateCourse.setBody(course.getBody());
				toUpdateCourse.setCertificate(course.getCertificate());
				toUpdateCourse.setSubjects(course.getSubjects());
				toUpdateCourse.setCurriculum(course.getCurriculum());
				toUpdateCourse.setCourseType(course.getCourseType());
				toUpdateCourse.setInstalments(course.getInstalments());
				toUpdateCourse.setResolutions(course.getResolutions());
				toUpdateCourse.setFirstDueDate(course.getFirstDueDate());
				toUpdateCourse.setQtyRegistrationFees(course
						.getQtyRegistrationFees());

				this.getCourseDAO().update(toUpdateCourse);
				course = toUpdateCourse;
			}
		} else {
			toUpdateCourse.setCode(course.getCode());
			toUpdateCourse.setName(course.getName());
			toUpdateCourse.setBody(course.getBody());
			toUpdateCourse.setCertificate(course.getCertificate());
			toUpdateCourse.setSubjects(course.getSubjects());
			toUpdateCourse.setCurriculum(course.getCurriculum());
			toUpdateCourse.setCourseType(course.getCourseType());
			toUpdateCourse.setInstalments(course.getInstalments());
			toUpdateCourse.setResolutions(course.getResolutions());
			toUpdateCourse.setFirstDueDate(course.getFirstDueDate());
			toUpdateCourse.setQtyRegistrationFees(course
					.getQtyRegistrationFees());

			this.getCourseDAO().update(toUpdateCourse);
			course = toUpdateCourse;
		}

		return course;
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.GenericService#filter(java.util.List,
	 *      ar.uba.fi.posgrado.common.model.User)
	 */
	public List<Course> filter(List<Course> list, User user)
			throws PosgradoRuntimeException, PosgradoException {
		if (!user.hasBodyRole()) {
			return list;
		}
		final Body body = user.getBody();
		if (logger.isDebugEnabled()) {
			logger
					.debug("El usuario tiene el rol Ente. Filtrando la informacion exclusiva para el "
							+ "ente: " + body);
		}

		CollectionUtils.filter(list, new Predicate() {
			public boolean evaluate(Object object) {
				Course course = (Course) object;
				if (course.getBody().equals(body)) {
					return true;
				}

				return false;
			}
		});

		return list;
	}

	@Transactional(readOnly = true)
	public List<CourseType> getAllCourseTypes()
			throws PosgradoRuntimeException, PosgradoException {
		try {
			List<CourseType> response = courseTypeDAO.findAll();
			logger.info("Tipos de curso encontrados: " + response.size());
			return response;
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion buscando tipos de curso", e);
		}
	}

	@Transactional(readOnly = true)
	public CourseType getCourseTypeById(Long courseTypeId)
			throws PosgradoRuntimeException, PosgradoException {
		try {
			CourseType response = (CourseType) courseTypeDAO
					.findById(courseTypeId);
			logger.info("Tipo de curso con Id: " + courseTypeId
					+ ", encontrado con exito");
			return response;
		} catch (DAOException e) {
			throw new PosgradoException(
					"Excepcion buscando el tipo de curso con Id: "
							+ courseTypeId, e);
		}
	}

}
